package com.zjc.springboot.file.service;

import com.zjc.springboot.file.dto.DownloadFileInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.MessageDigest;

@Service("fileService")
@Slf4j
public class FileService {

    /**
     * 编码
     */
    private static final String UTF_8 = "UTF-8";

    /**
     * 文件上传路径(当前项目路径下，也可配置固定路径)
     */
    @Value("${file.uploadPath:/usr/local/upload/}")
    private String uploadPath;

    @Value("${file.downloadUrl}")
    private String downloadUrl;

    /**
     * 下载指定文件
     */
    private String downloadFile = "D:\\Download\\git.exe";

    /**
     * final string
     */
    private static final String RANGE = "Range";

    /**
     * 上传文件
     */
    public void upload(HttpServletRequest request, HttpServletResponse response, MultipartFile file) throws Exception {

        file.getSize();
        file.getInputStream();

        File folder = new File(uploadPath);
        if (!folder.exists()) {
            folder.mkdir();
        }
        String fileName = file.getOriginalFilename();
        String fileSuffix = fileName.substring(fileName.lastIndexOf("."));

        String fileMd5 = getMd5(file);
        String fileRealName = fileMd5 + fileSuffix;

        File fileRestore = new File(uploadPath + fileRealName);

        file.transferTo(fileRestore);


        String fileUrl = downloadUrl + "?fileRealName=" + fileRealName;

        // 返回结果
        response.setCharacterEncoding(UTF_8);
        response.getWriter().write(fileUrl);
    }


    /**
     * 文件下载
     *
     * @param request
     * @param response
     * @throws IOException
     */
    public void download(HttpServletRequest request, HttpServletResponse response, String fileRealName) throws IOException {
        // 获取文件
        File file = new File(uploadPath + fileRealName);
        // 获取下载文件信息
        DownloadFileInfo downloadFileInfo = getDownloadFileInfo(file.length(), request, response);
        // 设置响应头
        setResponse(response, file.getName(), downloadFileInfo);
        // 下载文件
        try (InputStream is = new BufferedInputStream(new FileInputStream(file)); OutputStream os = new BufferedOutputStream(response.getOutputStream())) {
            // 跳过已经读取文件
            is.skip(downloadFileInfo.getPos());
            byte[] buffer = new byte[1024];
            long sum = 0;
            // 读取
            while (sum < downloadFileInfo.getRangeLength()) {
                int length = is.read(buffer, 0, (downloadFileInfo.getRangeLength() - sum) <= buffer.length ? (int) (downloadFileInfo.getRangeLength() - sum) : buffer.length);
                sum = sum + length;
                os.write(buffer, 0, length);
            }
        }

    }

    /**
     * 有两个map，我要去判断里面相同键的值一致不一致，除了双重for循环，有没有别的好办法
     */
    private DownloadFileInfo getDownloadFileInfo(long fSize, HttpServletRequest request, HttpServletResponse response) {
        long pos = 0;
        long last = fSize - 1;
        // 判断前端是否需要分片下载
        if (request.getHeader(RANGE) != null) {

            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

            String numRange = request.getHeader(RANGE).replace("bytes=", "");
            String[] strRange = numRange.split("-");

            if (strRange.length == 2) {
                pos = Long.parseLong(strRange[0].trim());
                last = Long.parseLong(strRange[1].trim());
                // 若结束字节超出文件大小，取文件大小
                if (last > fSize - 1) {
                    last = fSize - 1;
                }
            } else {
                // 若只给一个长度，开始位置一直到结束
                pos = Long.parseLong(numRange.replace("-", "").trim());
            }
        }
        long rangeLength = last - pos + 1;
        String contentRange = "bytes " + pos + "-" + last + "/" + fSize;
        return new DownloadFileInfo(fSize, pos, last, rangeLength, contentRange);
    }


    /**
     * 设置响应头
     */
    private void setResponse(HttpServletResponse response, String fileName, DownloadFileInfo downloadFileInfo) throws UnsupportedEncodingException {
        response.setCharacterEncoding(UTF_8);
        response.setContentType("application/x-download");
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, UTF_8));
        // 支持分片下载
        response.setHeader("Accept-Range", "bytes");
        response.setHeader("fSize", String.valueOf(downloadFileInfo.getFSize()));
        response.setHeader("fName", URLEncoder.encode(fileName, UTF_8));
        // range响应头
        response.setHeader("Content-Range", downloadFileInfo.getContentRange());
        response.setHeader("Content-Length", String.valueOf(downloadFileInfo.getRangeLength()));
    }


    /**
     * 获取上传文件的md5
     *
     * @param file
     * @return
     * @throws IOException
     */
    public String getMd5(MultipartFile file) {
        try {
            //获取文件的byte信息
            byte[] uploadBytes = file.getBytes();
            // 拿到一个MD5转换器
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] digest = md5.digest(uploadBytes);
            //转换为16进制
            return new BigInteger(1, digest).toString(16);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return null;
    }

}
